 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.dnd;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Monitor;
 import org.eclipse.swt.widgets.Shell;

 /**
  * Contains static methods for manipulating SWT controls
  *
  * @since 3.0
  */
 public class SwtUtil {

     private SwtUtil() {

     }

     /**
      * Returns true if the given control is null or has been disposed
      *
      * @param toTest the control to test
      * @return false if it is safe to invoke methods on the given control
      */
     public static boolean isDisposed(Control toTest) {
         return toTest == null || toTest.isDisposed();
     }

     /**
      * Returns the control that is covering the given control, or null if none.
      *
      * @param toTest control to test
      * @return a control that obscures the test control or null if none
      */
     public static Control controlThatCovers(Control toTest) {
         return controlThatCovers(toTest, DragUtil.getDisplayBounds(toTest));
     }
     
     private static Control controlThatCovers(Control toTest, Rectangle testRegion) {
         Composite parent = toTest.getParent();
         
         if (parent == null || toTest instanceof Shell) {
             return null;
         }
        
         Control[] children = parent.getChildren();
         for (int i = 0; i < children.length; i++) {
             Control control = children[i];
             
             if (control == toTest) {
                 break;
             }
             
             if (!control.isVisible()) {
                 continue;
             }
             
             Rectangle nextBounds = DragUtil.getDisplayBounds(control);
             
             if (nextBounds.intersects(testRegion)) {
                 return control;
             }
         }
         
         return controlThatCovers(parent, testRegion);
     }
     
     /**
      * Determines if one control is a child of another. Returns true iff the second
      * argument is a child of the first (or the same object).
      *
      * @param potentialParent
      * @param childToTest
      * @return
      */
     public static boolean isChild(Control potentialParent, Control childToTest) {
         if (childToTest == null) {
             return false;
         }

         if (childToTest == potentialParent) {
             return true;
         }

         return isChild(potentialParent, childToTest.getParent());
     }
     
     public static boolean isFocusAncestor(Control potentialParent) {
         Assert.isNotNull(potentialParent);
         Control focusControl = Display.getCurrent().getFocusControl();
         if (focusControl == null) {
             return false;
         }
         return isChild(potentialParent, focusControl);
     }

     /**
      * Finds and returns the most specific SWT control at the given location.
      * (Note: this does a DFS on the SWT widget hierarchy, which is slow).
      *
      * @param displayToSearch
      * @param locationToFind
      * @return
      */
     public static Control findControl(Display displayToSearch,
             Point locationToFind) {
         Shell[] shells = displayToSearch.getShells();

         return findControl(shells, locationToFind);
     }

     /**
      * Searches the given list of controls for a control containing the given point.
      * If the array contains any composites, those composites will be recursively
      * searched to find the most specific child that contains the point.
      *
      * @param toSearch an array of composites
      * @param locationToFind a point (in display coordinates)
      * @return the most specific Control that overlaps the given point, or null if none
      */
     public static Control findControl(Control[] toSearch, Point locationToFind) {
         for (int idx = toSearch.length - 1; idx >= 0; idx--) {
             Control next = toSearch[idx];

             if (!next.isDisposed() && next.isVisible()) {

                 Rectangle bounds = DragUtil.getDisplayBounds(next);

                 if (bounds.contains(locationToFind)) {
                     if (next instanceof Composite) {
                         Control result = findControl((Composite) next,
                                 locationToFind);

                         if (result != null) {
                             return result;
                         }
                     }

                     return next;
                 }
             }
         }

         return null;
     }

     public static Control[] getAncestors(Control theControl) {
         return getAncestors(theControl, 1);
     }
     
     private static Control[] getAncestors(Control theControl, int children) {
         Control[] result;
         
         if (theControl.getParent() == null) {
             result = new Control[children];
         } else {
             result = getAncestors(theControl.getParent(), children + 1);
         }
         
         result[result.length - children] = theControl;
         
         return result;
     }
     
     public static Control findCommonAncestor(Control control1, Control control2) {
         Control[] control1Ancestors = getAncestors(control1);
         Control[] control2Ancestors = getAncestors(control2);
         
         Control mostSpecific = null;
         
         for (int idx = 0; idx < Math.min(control1Ancestors.length, control2Ancestors.length); idx++) {
             Control control1Ancestor = control1Ancestors[idx];
             if (control1Ancestor == control2Ancestors[idx]) {
                 mostSpecific = control1Ancestor;
             } else {
                 break;
             }
         }
         
         return mostSpecific;
     }
     
     /**
      * Finds the control in the given location
      *
      * @param toSearch
      * @param locationToFind location (in display coordinates)
      * @return
      */
     public static Control findControl(Composite toSearch, Point locationToFind) {
         Control[] children = toSearch.getChildren();

         return findControl(children, locationToFind);
     }

     /**
      *
      * Returns true iff the given rectangle is located in the client area of any
      * monitor.
      *
      * @param someRectangle a rectangle in display coordinates (not null)
      * @return true iff the given point can be seen on any monitor
      */
     public static boolean intersectsAnyMonitor(Display display,
             Rectangle someRectangle) {
         Monitor[] monitors = display.getMonitors();
     
         for (int idx = 0; idx < monitors.length; idx++) {
             Monitor mon = monitors[idx];
     
             if (mon.getClientArea().intersects(someRectangle)) {
                 return true;
             }
         }
     
         return false;
     }

 }

